#include "Compositor.h"

#include <GLUT/glut.h>
#include "App.h"

namespace Y{

void Compositor::composite (){
	
	state.reset();
	geometry.reset();
	gl.reset();
	
	for ( iteratorLoop2(App->widgets,widgIt) ){
		if ( (*widgIt)->parent() == NULL ){
			widgetSubsystem((*widgIt));
		}
	}
	
	gl.triangleBuffer().resize( gl.tmpBuffer().size() );
	for ( int i = 0; i < gl.tmpBuffer().size(); ++i ){
		gl.triangleBuffer().acc(i) = gl.tmpBuffer()[i];
	}
	
	
	
}

void Compositor::widgetSubsystem (Widget *w){
	state.pushAll();
	geometry.pushAll();
	
	
	w->preDraw(this);
	ClippingPoly newClippingPoly = w->getClippingPoly();
	
	w->drawingSubsystem(this);
	
	w->postDraw();
	
	
	//These will hold the finished, clipped polygons and their associated textures
	std::vector < ComPoly > clippedPolyBuffer;
	std::vector < Texture * > clippedTextureBuffer;
	
	//If the widget is independent of its parent's bounds (as in the case of a windowBar widget) push one previous clipping
	//poly onto the clipping stack (i.e. the parent's parent)
	if ( w->drawingFlags.includes(PARENT_BOUNDS_INDEPENDENT) ){ geometry.pushClippingPoly( geometry.previousClippingPoly() ); }
	
	//If there are no clipping polys then this is a top level widget and need not attempt to clip
	//Likewise if this is a bounds independent widget (i.e. a submenu widget), no clipping is required
	if ( geometry.noClippingPoly() || w->drawingFlags.includes(BOUNDS_INDEPENDENT) ){ 
		clippedPolyBuffer = geometry.polyBuffer();clippedTextureBuffer = geometry.textureBuffer(); 
	}	//Otherwise, clip each poly against the current clippingPoly (usually the one defined by the direct parent)
	else{
		typeof ( geometry.textureBuffer().begin() ) texIt = geometry.textureBuffer().begin();
		for ( iteratorLoop2( geometry.polyBuffer(), polyIt ) ){
			
			ComPoly cp = geometry.clippingPoly().clipInclusively( (*polyIt) );
			if ( cp.size() == 0 ){
			}
			else{
				clippedPolyBuffer.push_back( cp );
				clippedTextureBuffer.push_back( *texIt );
			}
			++texIt;
		}
	}
	
	for ( int i = 0; i < clippedPolyBuffer.size(); ++i ){
		convertTextureCoordinates( clippedPolyBuffer[i],clippedTextureBuffer[i] );
	}
	
	for ( iteratorLoop2( clippedPolyBuffer,polyIt ) ){
		ComPoly& p = *polyIt;
		
		glVertex_tcnv avg;
		for ( int i = 0; i < p.size(); ++i ){
			avg += p[i];
		}
		avg /= (float) (polyIt->size());
		
		for ( int i = 0; i < p.size(); ++i ){
			gl.tmpBuffer().push_back( p[i] );
			gl.tmpBuffer().push_back( p[(i + 1) % p.size()] );
			gl.tmpBuffer().push_back( avg );
		}
	}
	
	geometry.pushClippingPoly(newClippingPoly);
	
	for ( iteratorLoop2( w->children, childIt ) ){
		widgetSubsystem( (Widget*)*childIt );
	}
	
	geometry.popClippingPoly();
	
	geometry.popAll();
	state.popAll();
}

void Compositor::convertTextureCoordinates (ComPoly& p,Texture *t){
	if ( t == NULL ){ t = App->getTexture("zero"); }
	TexCoordBlock tcb = App->textureBlockStorage["ui texture block"]->texCoords(t);
	for ( int i = 0; i < p.size(); ++i ){
		p[i].texCoord.x() = tcb.position.x() + p[i].texCoord.x() * tcb.dimensions.x();
		p[i].texCoord.y() = tcb.position.y() + p[i].texCoord.y() * tcb.dimensions.y();
	}
}


void printer ( const Vertex3f& v ){
	std::cout << "(" << v.x() << "," << v.y() << "," << v.z() << ")";
}

void printer ( const Vertex4f& v ){
	std::cout << "(" << v.x() << "," << v.y() << "," << v.z() << "," << v.a() << ")";
}


void Compositor::draw (){

	glPushMatrix();
	
	glScalef(1,-1,1);
	glBindTexture(GL_TEXTURE_2D,App->textureBlockStorage["ui texture block"]->texID);
//	glBindTexture(GL_TEXTURE_2D,0);
	
	if ( gl.triangleBuffer().size() > 1 ){	
		gl.triangleBuffer().interleavedArrays();
		gl.triangleBuffer().draw(GL_TRIANGLES);
	}
	
	glBindTexture(GL_TEXTURE_2D,0);
	
	glPopMatrix();
}

}